package edu.hit.stu.util.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import edu.hit.stu.util.common.IPrint;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 刘智彬
 */
@Data
@Component
@ConfigurationPropertiesScan(basePackageClasses = JwtProperties.class)
public class JwtUtil {

    static int minutes = 30;
    static String JWT_KEY = "$lzb$b36YXq.Li_7:b9c";

    private final JwtProperties jwtProperties;

    public JwtUtil(JwtProperties jwtProperties) {
        this.jwtProperties = jwtProperties;
        minutes = jwtProperties.getMins();
        JWT_KEY = jwtProperties.getJwtKey();
    }

    /**
     * 修改默认属性
     * <ul>
     *     <li>过期时间</li>
     *     <li>盐值</li>
     * </ul>
     * @param mins 过期时间（分钟）
     * @param salt 盐值
     */
    public JwtUtil changeAttr(int mins, String salt) {
        minutes = mins;
        JWT_KEY = salt;
        return this;
    }

    /**
     * 根据 用户信息 和 盐值 生成一个 JWT Token
     * @param userDetails 用户信息
     * @return JWT Token
     */
    public static String creatToken(UserDetails userDetails) {
        HashMap<String, Object> header = new HashMap<>();
        header.put("typ", "JWT");
        header.put("alg", "HS256");

        Date date = new Date();
        Instant plus = date.toInstant().plus(Duration.ofMinutes(minutes));
        date = Date.from(plus);

        String username = userDetails.getUsername();
        Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
        List<String> authoritiesStrs = authorities.stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList());
        return JWT.create()
                .withHeader(header)
                .withSubject(userDetails.getUsername())
                .withClaim("authorities", authoritiesStrs)
                .withExpiresAt(date)
                .sign(Algorithm.HMAC256(JWT_KEY));
    }

    /**
     * 根据 JWT 和 盐值 进行验证
     * @param token JWT Token
     * @return JWT是否被修改过?
     * <ul>
     *    <li>未被修改过: true</li>
     *    <li>修改过: false</li>
     * <ul/>
     */
    public static JwtStatus verifyJWT(String token) {
        try {
//            创建验证对象,这里使用的加密算法和密钥必须与生成TOKEN时的相同否则无法验证
            JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(JWT_KEY)).build();
//            验证JWT
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
        } catch (TokenExpiredException e) {
            return JwtStatus.EXPIRED;
        } catch (Exception err) {
            IPrint.printWithColor(err.getMessage());
            return JwtStatus.NOT_MATCH;
        }
        return JwtStatus.MATCH;
    }

    /**
     * 从 JWT 中解析 用户信息
     * @param token JWT
     * @return 解析过的 JWT
     */
    public static DecodedJWT getJWT(String token) {
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(JWT_KEY)).build();
        return jwtVerifier.verify(token);
    }
}

